﻿/************************************************************************************//**
* \file         main.c
* \brief        BootCommander program source file.
* \internal
*----------------------------------------------------------------------------------------
*                          C O P Y R I G H T
*----------------------------------------------------------------------------------------
*   Copyright (c) 2017  by Feaser    http://www.feaser.com    All rights reserved
*
*----------------------------------------------------------------------------------------
*                            L I C E N S E
*----------------------------------------------------------------------------------------
* This file is part of OpenBLT. OpenBLT is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* OpenBLT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You have received a copy of the GNU General Public License along with OpenBLT. It
* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy.
*
* \endinternal
****************************************************************************************/

/****************************************************************************************
* Include files
****************************************************************************************/
#include <assert.h>                         /* for assertions                          */
#include <stdint.h>                         /* for standard integer types              */
#include <stdbool.h>                        /* for boolean type                        */
#include <stdlib.h>                         /* for standard library                    */
#include <stdio.h>                          /* Standard I/O functions.                 */
#include <string.h>                         /* for string library                      */
#include "openblt.h"                        /* OpenBLT host library.                   */
#include "Qss.h"
#include "blt.h"
extern "C"{
    #include "modbus.h"
}
#include <QSerialPort>
#include <QTime>
#include <QTimer>

#pragma execution_character_set("utf-8")

/****************************************************************************************
* Macro definitions
****************************************************************************************/
/** \brief Program return code indicating that the program executed successfully. */
#define RESULT_OK                           (0)
/** \brief Program return code indicating that an error was detected when processing the
 *         command line parameters/
 */
#define RESULT_ERROR_COMMANDLINE            (1)
/** \brief Program return code indicating that an error was detected while loading
 *         firmware data from the firmware file.
 */
#define RESULT_ERROR_FIRMWARE_LOAD          (2)
/** \brief Program return code indicating that an error was detected during a memory
 *         erase operation on the target.
 */
#define RESULT_ERROR_MEMORY_ERASE           (3)
/** \brief Program return code indicating that an error was detected during a memory
 *         progrma operation on the target.
 */
#define RESULT_ERROR_MEMORY_PROGRAM         (4)

/* Macros for colored text on the output, if supported. */
#if defined (PLATFORM_LINUX)
#define OUTPUT_RESET                        "\033[0m"
#define OUTPUT_RED                          "\033[31m"
#define OUTPUT_GREEN                        "\033[32m"
#define OUTPUT_YELLOW                       "\033[33m"
#else
#define OUTPUT_RESET                        ""
#define OUTPUT_RED                          ""
#define OUTPUT_GREEN                        ""
#define OUTPUT_YELLOW                       ""
#endif


/****************************************************************************************
* Type definitions
****************************************************************************************/
/** \brief Type for program settings. */
typedef struct t_program_settings
{
  /* \brief Controls what gets written to the standard output. If set to false then all
   *        information is written. If set to true then only the most basic progress
   *        information is shows.
   */
  bool silentMode;
} tProgramSettings;


/****************************************************************************************
* Function prototypes
****************************************************************************************/
static void DisplayFirmwareDataInfo(uint32_t segments, uint32_t base, uint32_t size);


static char const * GetLineTrailerByResult(bool errorDetected);
static char const * GetLineTrailerByPercentage(uint8_t percentage);
static void ErasePercentageTrailer(void);





// 补上传数
void UploadUnSendData::Run(void *v) {
    result = RESULT_OK;
    tProgramSettings appProgramSettings = { 0 };
    uint32_t appSessionType = 0;
    tBltSessionSettingsXcpV10 appSessionSettings ;
    uint32_t appTransportType = 0;;
    tBltTransportSettingsXcpV10Rs232  appTransportSettings;
    char const * appFirmwareFile = (char*)malloc(1024);


    strcpy((char*)appFirmwareFile,mApp.toStdString().c_str());

    appSessionSettings.timeoutT1 = 500;
    appSessionSettings.timeoutT3 = 1000;
    appSessionSettings.timeoutT4 = 10000;
    appSessionSettings.timeoutT5 = 1000;
    appSessionSettings.timeoutT6 = 300;
    appSessionSettings.timeoutT7 = 2000;
    appSessionSettings.seedKeyFile = (char*)malloc(100);
    strcpy((char*)appSessionSettings.seedKeyFile,"libseednkey.dll");
    appSessionSettings.connectMode = 0;

    appTransportSettings.baudrate = mBaurate;
    appTransportSettings.portName = (char*)malloc(15);
    strcpy((char*)appTransportSettings.portName,mCom.toStdString().c_str());
    appTransportType = BLT_TRANSPORT_XCP_V10_RS232;
    appSessionType = BLT_SESSION_XCP_V10;


    emit(Error(QString::asprintf("Processing command line parameters...")));
    emit(Error(QString::asprintf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)))));

    /* -------------------- Display detected parameters -------------------------------- */
    if ( (result == RESULT_OK) && (!appProgramSettings.silentMode) )
    {
      /* Display firmware file. */
      emit(Error(QString::asprintf("Detected firmware file: %s\n", appFirmwareFile)));
      /* Display session info. */
      /* Display transport info. */
    }

    /* -------------------- Firmware loading ------------------------------------------- */
    if (result == RESULT_OK)
    {
      emit(Error(QString::asprintf("Loading firmware data from file...")));
      (void)fflush(stdout);
      /* Initialize the firmware data module using the S-record parser. */
      BltFirmwareInit(BLT_FIRMWARE_PARSER_SRECORD);
      /* Load firmware data from the firmware file without memory address offset. */
      if (BltFirmwareLoadFromFile(appFirmwareFile, 0) != BLT_RESULT_OK)
      {
        /* Set error code. */
        result = RESULT_ERROR_FIRMWARE_LOAD;
      }
      /* Check to make sure that data was actually present, in which case at least one
       * firmware data segment should be there.
       */
      if (result == RESULT_OK)
      {
        if (BltFirmwareGetSegmentCount() == 0)
        {
          /* Set error code. */
          result = RESULT_ERROR_FIRMWARE_LOAD;
        }
      }
      emit(Error(QString::asprintf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)))));
      /* Determine and output firmware data statistics. */
      if (result == RESULT_OK)
      {
        uint32_t firmwareDataTotalSize;
        uint32_t firmwareDataTotalSegments;
        uint32_t firmwareDataBaseAddress = 0;
        uint32_t segmentIdx;
        uint32_t segmentLen;
        uint32_t segmentBase;
        uint8_t const * segmentData;

        /* Store the number of segments. */
        firmwareDataTotalSegments = BltFirmwareGetSegmentCount();
        /* Loop through all segments. */
        firmwareDataTotalSize = 0;
        for (segmentIdx = 0; segmentIdx < firmwareDataTotalSegments; segmentIdx++)
        {
          /* Extract segment info. */
          segmentData = BltFirmwareGetSegment(segmentIdx, &segmentBase, &segmentLen);
          /* Sanity check. */
          assert( (segmentData != NULL) && (segmentLen > 0) );
          /* Update total size. */
          firmwareDataTotalSize += segmentLen;
          /* If it is the first segment, then store the base address. */
          if (segmentIdx == 0)
          {
            firmwareDataBaseAddress = segmentBase;
          }
        }
        /* Sanity check. */
        assert(firmwareDataTotalSize > 0);
        /* Output firmware data information. */
        if (!appProgramSettings.silentMode)
        {
          DisplayFirmwareDataInfo(firmwareDataTotalSegments, firmwareDataBaseAddress,
                                  firmwareDataTotalSize);
        }
      }
    }

    /* -------------------- Session starting ------------------------------------------- */
    if (result == RESULT_OK)
    {
      /* Initialize the session. */
      emit(Error(QString::asprintf("Connecting to target bootloader...")));
    (void)fflush(stdout);
      BltSessionInit(appSessionType,&appSessionSettings,
                    appTransportType, &appTransportSettings);
      /* Start the session. */
      if (BltSessionStart() != BLT_RESULT_OK)
      {
        emit(Error(QString::asprintf("[" OUTPUT_YELLOW "TIMEOUT" OUTPUT_RESET "]\n")));
        /* No response. Prompt the user to reset the system. */
        emit(Error(QString::asprintf("Attempting backdoor entry (reset system if this takes too long)...")));
        (void)fflush(stdout);
        /* Now keep trying until we get a response. */
        while (BltSessionStart() != BLT_RESULT_OK)
        {
          /* Delay a bit to not pump up the CPU load. */
          BltUtilTimeDelayMs(20);
        }
      }
      emit(Error(QString::asprintf("%s\n", GetLineTrailerByResult((bool)false))));
    }

    /* -------------------- Erase operation -------------------------------------------- */
    if (result == RESULT_OK)
    {
      uint32_t segmentIdx;
      uint32_t segmentLen;
      uint32_t segmentBase;
      uint8_t const * segmentData;

      /* Erase the memory segments on the target that are covered by the firmwware data. */
      for (segmentIdx = 0; segmentIdx < BltFirmwareGetSegmentCount(); segmentIdx++)
      {
        /* Extract segment info. */
        segmentData = BltFirmwareGetSegment(segmentIdx, &segmentBase, &segmentLen);
        /* Sanity check. */
        assert( (segmentData != NULL) && (segmentLen > 0) );
        /* Only continue if sanity check passed. */
        if ((segmentData != NULL) && (segmentLen > 0)) /*lint !e774 */
        {
          /* Perform erase operation. */
          emit(Error(QString::asprintf("Erasing %u bytes starting at 0x%08x...%s", segmentLen, segmentBase,
              GetLineTrailerByPercentage(0))));
          (void)fflush(stdout);
          /* Perform erase operation in chunks, so that a progress update can be shown
           * and no erase timeout occurs due to erasing too big of a memory range.
           */
          uint32_t const eraseChunkSize = 32768;
          uint32_t currentEraseCnt;
          uint32_t currentEraseBase;
          uint32_t currentEraseResult;
          uint32_t stillToEraseCnt;

          stillToEraseCnt = segmentLen;
          currentEraseBase = segmentBase;
          while (stillToEraseCnt > 0)
          {
            /* Determine chunk size. */
            if (stillToEraseCnt >= eraseChunkSize)
            {
              currentEraseCnt = eraseChunkSize;
            }
            else
            {
              currentEraseCnt = stillToEraseCnt;
            }
            /* Erase the next chunk from the target's memory. */
            currentEraseResult = BltSessionClearMemory(currentEraseBase, currentEraseCnt);
            if (currentEraseResult != BLT_RESULT_OK)
            {
              /* Set error code. */
              result = RESULT_ERROR_MEMORY_ERASE;
              /* Error detected so abort erase operation. */
              break;
            }
            /* Update loop variables. */
            currentEraseBase += currentEraseCnt;
            stillToEraseCnt -= currentEraseCnt;
            /* Display progress. */
            uint8_t progressPct;

            /* First backspace the old percentage trailer. */
            ErasePercentageTrailer();
            /* Now add the new percentage trailer. */
            progressPct = (uint8_t)(((segmentLen - stillToEraseCnt) * 100ul) / segmentLen);
            emit(Error(QString::asprintf("%s", GetLineTrailerByPercentage(progressPct)))); (void)fflush(stdout);
          }
        }
        else
        {
          /* Set error code because sanity check failed. */
          result = RESULT_ERROR_MEMORY_ERASE;
        }
        ErasePercentageTrailer();
        emit(Error(QString::asprintf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)))));
        /* Do not continue loop if an error was detected. */
        if (result != RESULT_OK)
        {
          break;
        }
      }
    }

    /* -------------------- Program operation ------------------------------------------ */
    if (result == RESULT_OK)
    {
      uint32_t segmentIdx;
      uint32_t segmentLen;
      uint32_t segmentBase;
      uint8_t const * segmentData;

      /* Program the memory segments on the target with the firmware data. */
      for (segmentIdx = 0; segmentIdx < BltFirmwareGetSegmentCount(); segmentIdx++)
      {
        /* Extract segment info. */
        segmentData = BltFirmwareGetSegment(segmentIdx, &segmentBase, &segmentLen);
        /* Sanity check. */
        assert( (segmentData != NULL) && (segmentLen > 0) );
        /* Only continue if sanity check passed. */
        if ((segmentData != NULL) && (segmentLen > 0)) /*lint !e774 */
        {
          emit(Error(QString::asprintf("Programming %u bytes starting at 0x%08x...%s", segmentLen, segmentBase,
            GetLineTrailerByPercentage(0))));
        qDebug()<<QString( GetLineTrailerByPercentage(0));
        emit(DonePercent(QString( GetLineTrailerByPercentage(0)).toInt()));
          (void)fflush(stdout);
          /* Perform write operation in chunks, so that a progress update can be shown. */
          uint32_t const writeChunkSize = 256;
          uint32_t currentWriteCnt;
          uint32_t currentWriteBase;
          uint8_t const * currentWriteDataPtr;
          uint32_t currentWriteResult;
          uint32_t stillToWriteCnt;

          stillToWriteCnt = segmentLen;
          currentWriteBase = segmentBase;
          currentWriteDataPtr = segmentData;
          while (stillToWriteCnt > 0)
          {
            /* Determine chunk size. */
            if (stillToWriteCnt >= writeChunkSize)
            {
              currentWriteCnt = writeChunkSize;
            }
            else
            {
              currentWriteCnt = stillToWriteCnt;
            }
            /* Write the next data chunk to the target's memory. */
            currentWriteResult = BltSessionWriteData(currentWriteBase, currentWriteCnt,
              currentWriteDataPtr);
            if (currentWriteResult != BLT_RESULT_OK)
            {
              /* Set error code. */
              result = RESULT_ERROR_MEMORY_PROGRAM;
              /* Error detected so abort program operation. */
              break;
            }
            /* Update loop variables. */
            currentWriteBase += currentWriteCnt;
            currentWriteDataPtr += currentWriteCnt;
            stillToWriteCnt -= currentWriteCnt;
            /* Display progress. */
            uint8_t progressPct;

            /* First backspace the old percentage trailer. */
            ErasePercentageTrailer();
            /* Now add the new percentage trailer. */
            progressPct = (uint8_t)(((segmentLen - stillToWriteCnt) * 100ul) / segmentLen);
            emit(DonePercent(progressPct));
            emit(Error(QString::asprintf("%s", GetLineTrailerByPercentage(progressPct)))); (void)fflush(stdout);
          }
        }
        else
        {
          /* Set error code because sanity check failed. */
          result = RESULT_ERROR_MEMORY_PROGRAM;
        }
        ErasePercentageTrailer();
        emit(Error(QString::asprintf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)))));
        qDebug()<<GetLineTrailerByResult((bool)(result != RESULT_OK));
        emit(DonePercent(QString( GetLineTrailerByResult((bool)(result != RESULT_OK))).toInt()));

        /* Do not continue loop if an error was detected. */
        if (result != RESULT_OK)
        {
          break;
        }
      }
    }

    /* -------------------- Session stopping ------------------------------------------- */
    if (result == RESULT_OK)
    {
      /* Stop the session. */
      emit(Error(QString::asprintf("Finishing programming session..."))); (void)fflush(stdout);
      BltSessionStop();
      emit(Error(QString::asprintf("%s\n", GetLineTrailerByResult((bool)false))));
    }

    /* -------------------- Cleanup ---------------------------------------------------- */
    /* Terminate the session. */
    BltSessionTerminate();
    /* Terminate the firmware data module. */
    BltFirmwareTerminate();



    emit(Done());
    /* Free allocated memory */
    /* Give result back. */
}


/************************************************************************************//**
** \brief     This is the program entry point.
** \param     argc Number of program arguments.
** \param     argv Array with program arguments.
** \return    Program return code. 0 for success, error code otherwise.
**
****************************************************************************************/
int Program(const char*app,const char * com,int baurate)
{
  int result = RESULT_OK;
  tProgramSettings appProgramSettings = { 0 };
  uint32_t appSessionType = 0;
  tBltSessionSettingsXcpV10 appSessionSettings ;
  uint32_t appTransportType = 0;;
  tBltTransportSettingsXcpV10Rs232  appTransportSettings;
  char const * appFirmwareFile = (char*)malloc(1024);

  if(NULL == app)
    return -1;
  if(NULL == com)
    return -1;

//   strcpy(appFirmwareFile,"c:\\Users\\Administrator\\Desktop\\MDCS_V1_6_4_85.S19");
  strcpy((char*)appFirmwareFile,app);

  appSessionSettings.timeoutT1 = 1000;
  appSessionSettings.timeoutT3 = 2000;
  appSessionSettings.timeoutT4 = 10000;
  appSessionSettings.timeoutT5 = 1000;
  appSessionSettings.timeoutT6 = 1000;
  appSessionSettings.timeoutT7 = 2000;
  appSessionSettings.seedKeyFile = (char*)malloc(100);
  strcpy((char*)appSessionSettings.seedKeyFile,"libseednkey.dll");
  appSessionSettings.connectMode = 0;

  appTransportSettings.baudrate = baurate;
  appTransportSettings.portName = (char*)malloc(15);
  strcpy((char*)appTransportSettings.portName,com);
  appTransportType = BLT_TRANSPORT_XCP_V10_RS232;
  appSessionType = BLT_SESSION_XCP_V10;


//  emit(Error(QString::asprintf("Processing command line parameters...")));
  printf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)));

  /* -------------------- Display detected parameters -------------------------------- */
  if ( (result == RESULT_OK) && (!appProgramSettings.silentMode) )
  {
    /* Display firmware file. */
    printf("Detected firmware file: %s\n", appFirmwareFile);
    /* Display session info. */
    /* Display transport info. */
  }

  /* -------------------- Firmware loading ------------------------------------------- */
  if (result == RESULT_OK)
  {
    printf("Loading firmware data from file..."); (void)fflush(stdout);
    /* Initialize the firmware data module using the S-record parser. */
    BltFirmwareInit(BLT_FIRMWARE_PARSER_SRECORD);
    /* Load firmware data from the firmware file without memory address offset. */
    if (BltFirmwareLoadFromFile(appFirmwareFile, 0) != BLT_RESULT_OK)
    {
      /* Set error code. */
      result = RESULT_ERROR_FIRMWARE_LOAD;
    }
    /* Check to make sure that data was actually present, in which case at least one
     * firmware data segment should be there.
     */
    if (result == RESULT_OK)
    {
      if (BltFirmwareGetSegmentCount() == 0)
      {
        /* Set error code. */
        result = RESULT_ERROR_FIRMWARE_LOAD;
      }
    }
    printf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)));
    /* Determine and output firmware data statistics. */
    if (result == RESULT_OK)
    {
      uint32_t firmwareDataTotalSize;
      uint32_t firmwareDataTotalSegments;
      uint32_t firmwareDataBaseAddress = 0;
      uint32_t segmentIdx;
      uint32_t segmentLen;
      uint32_t segmentBase;
      uint8_t const * segmentData;

      /* Store the number of segments. */
      firmwareDataTotalSegments = BltFirmwareGetSegmentCount();
      /* Loop through all segments. */
      firmwareDataTotalSize = 0;
      for (segmentIdx = 0; segmentIdx < firmwareDataTotalSegments; segmentIdx++)
      {
        /* Extract segment info. */
        segmentData = BltFirmwareGetSegment(segmentIdx, &segmentBase, &segmentLen);
        /* Sanity check. */
        assert( (segmentData != NULL) && (segmentLen > 0) );
        /* Update total size. */
        firmwareDataTotalSize += segmentLen;
        /* If it is the first segment, then store the base address. */
        if (segmentIdx == 0)
        {
          firmwareDataBaseAddress = segmentBase;
        }
      }
      /* Sanity check. */
      assert(firmwareDataTotalSize > 0);
      /* Output firmware data information. */
      if (!appProgramSettings.silentMode)
      {
        DisplayFirmwareDataInfo(firmwareDataTotalSegments, firmwareDataBaseAddress,
                                firmwareDataTotalSize);
      }
    }
  }

  /* -------------------- Session starting ------------------------------------------- */
  if (result == RESULT_OK)
  {
    /* Initialize the session. */
    printf("Connecting to target bootloader..."); (void)fflush(stdout);
    BltSessionInit(appSessionType,&appSessionSettings,
                  appTransportType, &appTransportSettings);
    /* Start the session. */
    if (BltSessionStart() != BLT_RESULT_OK)
    {
      printf("[" OUTPUT_YELLOW "TIMEOUT" OUTPUT_RESET "]\n");
      /* No response. Prompt the user to reset the system. */
      printf("Attempting backdoor entry (reset system if this takes too long)...");
      (void)fflush(stdout);

      /* Now keep trying until we get a response. */
      while (BltSessionStart() != BLT_RESULT_OK)
      {
        /* Delay a bit to not pump up the CPU load. */
        BltUtilTimeDelayMs(20);
      }
    }
    printf("%s\n", GetLineTrailerByResult((bool)false));
  }

  /* -------------------- Erase operation -------------------------------------------- */
  if (result == RESULT_OK)
  {
    uint32_t segmentIdx;
    uint32_t segmentLen;
    uint32_t segmentBase;
    uint8_t const * segmentData;

    /* Erase the memory segments on the target that are covered by the firmwware data. */
    for (segmentIdx = 0; segmentIdx < BltFirmwareGetSegmentCount(); segmentIdx++)
    {
      /* Extract segment info. */
      segmentData = BltFirmwareGetSegment(segmentIdx, &segmentBase, &segmentLen);
      /* Sanity check. */
      assert( (segmentData != NULL) && (segmentLen > 0) );
      /* Only continue if sanity check passed. */
      if ((segmentData != NULL) && (segmentLen > 0)) /*lint !e774 */
      {
        /* Perform erase operation. */
        printf("Erasing %u bytes starting at 0x%08x...%s", segmentLen, segmentBase,
            GetLineTrailerByPercentage(0));
        (void)fflush(stdout);
        /* Perform erase operation in chunks, so that a progress update can be shown
         * and no erase timeout occurs due to erasing too big of a memory range.
         */
        uint32_t const eraseChunkSize = 32768;
        uint32_t currentEraseCnt;
        uint32_t currentEraseBase;
        uint32_t currentEraseResult;
        uint32_t stillToEraseCnt;

        stillToEraseCnt = segmentLen;
        currentEraseBase = segmentBase;
        while (stillToEraseCnt > 0)
        {
          /* Determine chunk size. */
          if (stillToEraseCnt >= eraseChunkSize)
          {
            currentEraseCnt = eraseChunkSize;
          }
          else
          {
            currentEraseCnt = stillToEraseCnt;
          }
          /* Erase the next chunk from the target's memory. */
          currentEraseResult = BltSessionClearMemory(currentEraseBase, currentEraseCnt);
          if (currentEraseResult != BLT_RESULT_OK)
          {
            /* Set error code. */
            result = RESULT_ERROR_MEMORY_ERASE;
            /* Error detected so abort erase operation. */
            break;
          }
          /* Update loop variables. */
          currentEraseBase += currentEraseCnt;
          stillToEraseCnt -= currentEraseCnt;
          /* Display progress. */
          uint8_t progressPct;

          /* First backspace the old percentage trailer. */
          ErasePercentageTrailer();
          /* Now add the new percentage trailer. */
          progressPct = (uint8_t)(((segmentLen - stillToEraseCnt) * 100ul) / segmentLen);
          printf("%s", GetLineTrailerByPercentage(progressPct)); (void)fflush(stdout);
        }
      }
      else
      {
        /* Set error code because sanity check failed. */
        result = RESULT_ERROR_MEMORY_ERASE;
      }
      ErasePercentageTrailer();
      printf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)));
      /* Do not continue loop if an error was detected. */
      if (result != RESULT_OK)
      {
        break;
      }
    }
  }

  /* -------------------- Program operation ------------------------------------------ */
  if (result == RESULT_OK)
  {
    uint32_t segmentIdx;
    uint32_t segmentLen;
    uint32_t segmentBase;
    uint8_t const * segmentData;

    /* Program the memory segments on the target with the firmware data. */
    for (segmentIdx = 0; segmentIdx < BltFirmwareGetSegmentCount(); segmentIdx++)
    {
      /* Extract segment info. */
      segmentData = BltFirmwareGetSegment(segmentIdx, &segmentBase, &segmentLen);
      /* Sanity check. */
      assert( (segmentData != NULL) && (segmentLen > 0) );
      /* Only continue if sanity check passed. */
      if ((segmentData != NULL) && (segmentLen > 0)) /*lint !e774 */
      {
        printf("Programming %u bytes starting at 0x%08x...%s", segmentLen, segmentBase,
          GetLineTrailerByPercentage(0));
        (void)fflush(stdout);
        /* Perform write operation in chunks, so that a progress update can be shown. */
        uint32_t const writeChunkSize = 256;
        uint32_t currentWriteCnt;
        uint32_t currentWriteBase;
        uint8_t const * currentWriteDataPtr;
        uint32_t currentWriteResult;
        uint32_t stillToWriteCnt;

        stillToWriteCnt = segmentLen;
        currentWriteBase = segmentBase;
        currentWriteDataPtr = segmentData;
        while (stillToWriteCnt > 0)
        {
          /* Determine chunk size. */
          if (stillToWriteCnt >= writeChunkSize)
          {
            currentWriteCnt = writeChunkSize;
          }
          else
          {
            currentWriteCnt = stillToWriteCnt;
          }
          /* Write the next data chunk to the target's memory. */
          currentWriteResult = BltSessionWriteData(currentWriteBase, currentWriteCnt,
            currentWriteDataPtr);
          if (currentWriteResult != BLT_RESULT_OK)
          {
            /* Set error code. */
            result = RESULT_ERROR_MEMORY_PROGRAM;
            /* Error detected so abort program operation. */
            break;
          }
          /* Update loop variables. */
          currentWriteBase += currentWriteCnt;
          currentWriteDataPtr += currentWriteCnt;
          stillToWriteCnt -= currentWriteCnt;
          /* Display progress. */
          uint8_t progressPct;

          /* First backspace the old percentage trailer. */
          ErasePercentageTrailer();
          /* Now add the new percentage trailer. */
          progressPct = (uint8_t)(((segmentLen - stillToWriteCnt) * 100ul) / segmentLen);
          printf("%s", GetLineTrailerByPercentage(progressPct)); (void)fflush(stdout);
        }
      }
      else
      {
        /* Set error code because sanity check failed. */
        result = RESULT_ERROR_MEMORY_PROGRAM;
      }
      ErasePercentageTrailer();
      printf("%s\n", GetLineTrailerByResult((bool)(result != RESULT_OK)));
      /* Do not continue loop if an error was detected. */
      if (result != RESULT_OK)
      {
        break;
      }
    }
  }

  /* -------------------- Session stopping ------------------------------------------- */
  if (result == RESULT_OK)
  {
    /* Stop the session. */
    printf("Finishing programming session..."); (void)fflush(stdout);
    BltSessionStop();
    printf("%s\n", GetLineTrailerByResult((bool)false));
  }

  /* -------------------- Cleanup ---------------------------------------------------- */
  /* Terminate the session. */
  BltSessionTerminate();
  /* Terminate the firmware data module. */
  BltFirmwareTerminate();
  /* Free allocated memory */
  /* Give result back. */
  return result;
} /*** end of main ***/




/************************************************************************************//**
** \brief     Displays firmware data information on the standard output.
** \param     segments Total number of firmware data segments
** \param     base The base memory address of the firmware data.
** \param     size Total number of firmware data bytes.
**
****************************************************************************************/
static void DisplayFirmwareDataInfo(uint32_t segments, uint32_t base, uint32_t size)
{
  /* Output number of segments. */
  printf("  -> Number of segments: %u\n", segments);
  /* Output the base address. */
  printf("  -> Base memory address: 0x%08x\n", base);
  /* Ouput total firmware data size. */
  printf("  -> Total data size: %u bytes\n", size);
} /*** end of DisplayFirmwareDataInfo ***/


/************************************************************************************//**
** \brief     Parses the command line to extract the program settings.
** \param     argc Number of program arguments.
** \param     argv Array with program parameter strings.
** \param     programSettings Pointer to the setting structure where the program settings
**            should be written to.
**
****************************************************************************************/
static void ExtractProgramSettingsFromCommandLine(int argc, char const * const argv[],
                                                  tProgramSettings * programSettings)
{
  uint8_t paramIdx;

  /* Check parameters. */
  assert(argv != NULL);
  assert(programSettings != NULL);

  /* Only continue if parameters are valid. */
  if ( (argv != NULL) && (programSettings != NULL) ) /*lint !e774 */
  {
    /* Set default program settings. */
    programSettings->silentMode = false;
    /* Loop through all the command line parameters, just skip the 1st one because
     * this  is the name of the program, which we are not interested in.
     */
    for (paramIdx = 1; paramIdx < argc; paramIdx++)
    {
      /* Is this the -sm parameter? */
      if ( (strstr(argv[paramIdx], "-sm") != NULL) &&
           (strlen(argv[paramIdx]) == 3) )
      {
        /* Activate silent mode. */
        programSettings->silentMode = true;
        /* Continue with next loop iteration. */
        continue;
      }
    }
  }
} /*** end of ExtractProgramSettingsFromCommandLine ***/


/************************************************************************************//**
** \brief     Parses the command line to extract the firmware file. This is the only
**            parameter that is specified without a '-' character at the start.
** \param     argc Number of program arguments.
** \param     argv Array with program parameter strings.
** \return    Pointer to the character array (string) with the firmware file.
**
****************************************************************************************/
static char const * const ExtractFirmwareFileFromCommandLine(int argc,
                                                             char const * const argv[])
{
  char const * result = NULL;
  uint8_t paramIdx;

    /* Check parameters. */
  assert(argv != NULL);

  /* Only continue if parameters are valid. */
  if (argv != NULL) /*lint !e774 */
  {
    /* Loop through all the command line parameters, just skip the 1st one because this
     * is the name of the program, which we are not interested in.
     */
    for (paramIdx = 1; paramIdx < argc; paramIdx++)
    {
      /* Does this one not start with the '-' character and does it have a plausible
       * length?
       */
      if ( (argv[paramIdx][0] != '-') && (strlen(argv[paramIdx]) > 2) )
      {
        /* Store the pointer to the firmware file. */
        result = argv[paramIdx];
      }
    }
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of ExtractFirmwareFileFromCommandLine ***/


/************************************************************************************//**
** \brief     Information outputted to the user sometimes has [OK] or [ERROR] appended
**            at the end. This function obtains this trailer based on the value of the
**            parameter.
** \param     errorDetected True to obtain an error trailer, false for a success trailer.
** \return    Pointer to the character array (string) with the trailer.
**
****************************************************************************************/
static char const * GetLineTrailerByResult(bool errorDetected)
{
  char const * result;
  /* Note that the following strings were declared static to guarantee that the pointers
   * stay valid and can be used by the caller of this function.
   */
  static char const * trailerStrOk = "[" OUTPUT_GREEN "OK" OUTPUT_RESET "]";
  static char const * trailerStrError = "[" OUTPUT_RED "ERROR" OUTPUT_RESET "]";

  /* Set trailer based on the error status. */
  if (!errorDetected)
  {
    result = trailerStrOk;
  }
  else
  {
    result = trailerStrError;
  }
  /* Give the result back to the caller. */
  return result;
} /*** end of GetLineTrailerByResult ***/


/************************************************************************************//**
** \brief     Information outputted to the user sometimes has [xxx%] appended at the end.
**            This function obtains this trailer based on the value of the parameter.
** \param     percentage Percentage value (0..100) to embed in the trailer.
** \return    Pointer to the character array (string) with the trailer.
**
****************************************************************************************/
static char const * GetLineTrailerByPercentage(uint8_t percentage)
{
  char const * result;
  /* Note that the following string was declared static to guarantee that the pointer
   * stays valid and can be used by the caller of this function.
   */
  static char trailerStrPct[32] = "";

  /* Construct the trailer. */
  sprintf(trailerStrPct, "[" OUTPUT_YELLOW "%3hhu%%" OUTPUT_RESET "]", percentage);
  /* Set the result. */
  result = &trailerStrPct[0];
  /* Give the result back to the caller. */
  return result;
} /*** end of GetLineTrailerByPercentage ***/


/************************************************************************************//**
** \brief     Erases a percentage trailer from the standard ouput by means of writing
**            backspace characters.
**
****************************************************************************************/
static void ErasePercentageTrailer(void)
{
  uint32_t backspaceCnt;
  uint32_t trailerLen;

  trailerLen = strlen("[100%]");
  for (backspaceCnt = 0; backspaceCnt < trailerLen; backspaceCnt++)
  {
    /* Go one character back. */
    (void)putchar('\b');
    /* Overwrite it with a space. */
    (void)putchar(' ');
    /* Go one character back. */
    (void)putchar('\b');
  }
} /*** end of ErasePercentageTrailer ***/


/*********************************** end of main.c *************************************/


